﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MVCQuick.Framework.Container;
using MVCQuick.Framework.Repository;

namespace MVCQuick.Framework.Security
{
    /// <summary>
    /// 对 ASP.NET 应用程序的配置文件信息的存储进行管理。
    /// </summary>
    public class ClassicProfileProvider : System.Web.Profile.ProfileProvider
    {
        private string applicationName;       
        private IRepository repository;
      

        /// <summary>
        /// 获取或设置要存储和检索其配置文件信息的应用程序的名称。
        /// </summary>
        public override string ApplicationName
        {
            get
            {
                return applicationName;
            }
            set
            {
                if (String.IsNullOrEmpty(value))
                {
                    throw new ArgumentNullException("Provider application name not null.");
                }
                if (value.Length > 255)
                {
                    throw new System.Configuration.Provider.ProviderException(
                        "Provider application name too long.");
                }
                applicationName = value;

            }
        }

      

        /// <summary>
        /// 利用在 ASP.NET 应用程序的配置文件中指定的属性值初始化配置文件提供程序。
        /// 此方法不应从代码直接使用。
        /// </summary>
        /// <param name="name">要初始化的 ProfileProvider 实例的名称。</param>
        /// <param name="config">
        /// 一个 NameValueCollection，包含配置文件提供程序的配置选项的名称和值。 
        /// </param>
        public override void Initialize(string name,
            System.Collections.Specialized.NameValueCollection config)
        {
            if (config == null)
            {
                throw new ArgumentNullException("config");
            }

            if (String.IsNullOrEmpty(name))
            {
                name = "ClassicProfileProvider";
            }

            if (String.IsNullOrEmpty(config["description"]))
            {
                config.Remove("description");
                config.Add("description", "MVCQuick.Framework.Security Profile Provider");
            }

            base.Initialize(name, config);


            this.applicationName = config["applicationName"];
            if (String.IsNullOrEmpty(this.applicationName))
            {
                this.applicationName = SecUtility.GetDefaultAppName();
            }

            if (this.applicationName.Length > 255)
            {
                throw new System.Configuration.Provider.ProviderException(
                    "Provider application name is too long, max length is 255.");
            }

            string strRepository = config["repository"];
            if (strRepository != null)
            {
                repository = ClassicContainer.GetObject(strRepository) as IRepository;
            }
            else
            {
                repository = ClassicContainer.GetObject<IRepository>() as IRepository;
            }

            if (!new ApplicationService(repository).ApplicationExists(this.applicationName))
            {
                new ApplicationService(repository).CreateApplication(this.applicationName);
            }           

            config.Remove("repository");
            config.Remove("applicationName");           

            if (config.Count > 0)
            {
                string attribUnrecognized = config.GetKey(0);
                if (!String.IsNullOrEmpty(attribUnrecognized))
                {
                    throw new System.Configuration.Provider.ProviderException(
                        "Provider unrecognized attribute: " + attribUnrecognized);
                }
            }
        }

        /// <summary>
        /// 删除上次活动日期出现在指定日期和时间之前的配置文件的用户配置文件数据。
        /// </summary>
        /// <param name="authenticationOption">
        /// ProfileAuthenticationOption 值之一，指定是删除匿名配置文件、
        /// 已验证的配置文件还是同时删除这两种类型的配置文件。 
        /// </param>
        /// <param name="userInactiveSinceDate">
        /// 一个 DateTime，确定将哪些用户配置文件视为处于不活动状态。 
        /// 如果用户配置文件的 LastActivityDate 与此日期和时间相同，
        /// 或在此日期和时间之前，则此配置文件被视为处于不活动状态。 
        /// </param>
        /// <returns>从数据源中删除的配置文件的数目。</returns>
        public override int DeleteInactiveProfiles(
            System.Web.Profile.ProfileAuthenticationOption authenticationOption,
            DateTime userInactiveSinceDate)
        {
            return new ProfileService(repository).DeleteInactiveProfiles(this.applicationName,
                authenticationOption, userInactiveSinceDate);

        }

        /// <summary>
        /// 从数据源中删除提供的一系列配置文件的配置文件属性和信息。
        /// </summary>
        /// <param name="profiles">一个 ProfileInfoCollection，包含要删除的配置文件的配置文件信息。</param>
        /// <returns>从数据源中删除的配置文件的数目。</returns>
        public override int DeleteProfiles(System.Web.Profile.ProfileInfoCollection profiles)
        {
            if (profiles == null)
            {
                throw new ArgumentNullException("profiles");
            }
            if (profiles.Count < 1)
            {
                throw new ArgumentException("Parameter collection empty.");
            }
            string[] usernames = new string[profiles.Count];
            int index = 0;
            foreach (System.Web.Profile.ProfileInfo profileInfo in profiles)
            {
                usernames[index++] = profileInfo.UserName;
            }
            return DeleteProfiles(usernames);

        }

        /// <summary>
        /// 从数据源中删除提供的一系列用户名的配置文件属性和信息。
        /// </summary>
        /// <param name="usernames">要删除的配置文件的用户名字符串数组。</param>
        /// <returns>从数据源中删除的配置文件的数目。</returns>
        public override int DeleteProfiles(string[] usernames)
        {
            SecUtility.CheckArrayParameter(ref usernames, true, true, true, 0x100, "usernames");

            return new ProfileService(repository).DeleteProfiles(this.applicationName, usernames);

        }

        /// <summary>
        /// 检索一些配置文件的配置文件信息，在这些配置文件中，
        /// 上次活动日期与指定的日期和时间相同或在其之前，而且配置文件的用户名与指定的名称匹配。
        /// </summary>
        /// <param name="authenticationOption">
        /// ProfileAuthenticationOption 值之一，
        /// 指定是返回匿名配置文件、经过身份验证的配置文件还是同时返回这两种类型的配置文件。 
        /// </param>
        /// <param name="usernameToMatch">要搜索的用户名。</param>
        /// <param name="userInactiveSinceDate">
        /// 一个 DateTime，确定将哪些用户配置文件视为处于不活动状态。 
        /// 如果用户配置文件的 LastActivityDate 与此日期和时间相同，
        /// 或在此日期和时间之前，则此配置文件被视为处于不活动状态。 
        ///</param>
        /// <param name="pageIndex">要返回的结果页的索引。pageIndex 从零开始。</param>
        /// <param name="pageSize">要返回的结果页的大小。</param>
        /// <param name="totalRecords">
        /// 此方法返回时，会包含一个表示配置文件总数的整数。该参数未经初始化即被传递。
        ///</param>
        /// <returns>
        /// 包含不活动配置文件的用户配置文件信息的 ProfileInfoCollection，
        /// 这些不活动配置文件中的用户名与提供的 usernameToMatch 参数相匹配。 
        ///</returns>
        public override System.Web.Profile.ProfileInfoCollection FindInactiveProfilesByUserName(
            System.Web.Profile.ProfileAuthenticationOption authenticationOption,
            string usernameToMatch,
            DateTime userInactiveSinceDate,
            int pageIndex,
            int pageSize,
            out int totalRecords)
        {
            SecUtility.CheckParameter(ref usernameToMatch, true, true, false, 0x100, "username");

            System.Web.Profile.ProfileInfoCollection profileInfoCollection =
               new System.Web.Profile.ProfileInfoCollection();

            IList<System.Web.Profile.ProfileInfo> profileInfos =
                new ProfileService(repository).FindProfiles(
                    this.applicationName,
                    authenticationOption,
                    usernameToMatch,
                    userInactiveSinceDate,
                    pageIndex, 
                    pageIndex, 
                    out totalRecords);

            foreach (var profileInfo in profileInfos)
            {
                profileInfoCollection.Add(profileInfo);
            }

            return profileInfoCollection;

        }

        /// <summary>
        /// 检索用户名与指定名称匹配的配置文件的配置文件信息。
        /// </summary>
        /// <param name="authenticationOption">
        /// ProfileAuthenticationOption 值之一，
        /// 指定是返回匿名配置文件、经过身份验证的配置文件还是同时返回这两种类型的配置文件。
        /// </param>
        /// <param name="usernameToMatch">要搜索的用户名。</param>
        /// <param name="pageIndex">要返回的结果页的索引。pageIndex 从零开始。</param>
        /// <param name="pageSize">要返回的结果页的大小。</param>
        /// <param name="totalRecords">
        /// 此方法返回时，会包含一个表示配置文件总数的整数。该参数未经初始化即被传递。
        ///</param>
        /// <returns>
        /// 包含配置文件中的用户配置文件信息的 ProfileInfoCollection，
        /// 其中的用户名与提供的 usernameToMatch 参数相匹配。 
        /// </returns>
        public override System.Web.Profile.ProfileInfoCollection FindProfilesByUserName(
            System.Web.Profile.ProfileAuthenticationOption authenticationOption,
            string usernameToMatch,
            int pageIndex, int pageSize, out int totalRecords)
        {
            SecUtility.CheckParameter(ref usernameToMatch, true, true, false, 0x100, "username");

            System.Web.Profile.ProfileInfoCollection profileInfoCollection =
               new System.Web.Profile.ProfileInfoCollection();

            IList<System.Web.Profile.ProfileInfo> profileInfos =
                new ProfileService(repository).FindProfiles(
                    this.applicationName,
                    authenticationOption,
                    usernameToMatch,
                    pageIndex, 
                    pageIndex, 
                    out totalRecords);

            foreach (var profileInfo in profileInfos)
            {
                profileInfoCollection.Add(profileInfo);
            }

            return profileInfoCollection;
        }

        /// <summary>
        /// 对于上次活动日期与指定的日期和时间相同或在其之前的配置文件，检索它们的用户配置文件数据。

        /// </summary>
        /// <param name="authenticationOption">
        /// ProfileAuthenticationOption 值之一，
        /// 指定是返回匿名配置文件、经过身份验证的配置文件还是同时返回这两种类型的配置文件。 
        /// </param>
        /// <param name="userInactiveSinceDate">
        /// 一个 DateTime，确定将哪些用户配置文件视为处于不活动状态。 
        /// 如果用户配置文件的 LastActivityDate 与此日期和时间相同，
        /// 或在此日期和时间之前，则此配置文件被视为处于不活动状态。 
        /// </param>
        /// <param name="pageIndex">要返回的结果页的索引。pageIndex 从零开始。</param>
        /// <param name="pageSize">要返回的结果页的大小。</param>
        /// <param name="totalRecords">
        /// 此方法返回时，会包含一个表示配置文件总数的整数。该参数未经初始化即被传递。
        ///</param>
        /// <returns>
        /// 一个 ProfileInfoCollection，包含有关不活动的配置文件的用户配置文件信息。 
        /// </returns>
        public override System.Web.Profile.ProfileInfoCollection GetAllInactiveProfiles(
            System.Web.Profile.ProfileAuthenticationOption authenticationOption,
            DateTime userInactiveSinceDate,
            int pageIndex, int pageSize, out int totalRecords)
        {
            System.Web.Profile.ProfileInfoCollection profileInfoCollection =
              new System.Web.Profile.ProfileInfoCollection();

            IList<System.Web.Profile.ProfileInfo> profileInfos =
                new ProfileService(repository).GetAllInactiveProfiles(
                    this.applicationName,
                    authenticationOption,
                    userInactiveSinceDate,
                    pageIndex, 
                    pageIndex, 
                    out totalRecords);

            foreach (var profileInfo in profileInfos)
            {
                profileInfoCollection.Add(profileInfo);
            }

            return profileInfoCollection;
        }

        /// <summary>
        /// 在数据源中检索配置文件的用户配置文件数据。
        /// </summary>
        /// <param name="authenticationOption">
        /// ProfileAuthenticationOption 值之一，
        /// 指定是返回匿名配置文件、经过身份验证的配置文件还是同时返回这两种类型的配置文件。
        /// </param>
        /// <param name="pageIndex">要返回的结果页的索引。pageIndex 从零开始。</param>
        /// <param name="pageSize">要返回的结果页的大小。</param>
        /// <param name="totalRecords">
        /// 此方法返回时，会包含一个表示配置文件总数的整数。该参数未经初始化即被传递。
        ///</param>
        /// <returns>
        /// 一个 ProfileInfoCollection，包含数据源中所有配置文件的用户配置文件信息。 
        /// </returns>
        public override System.Web.Profile.ProfileInfoCollection GetAllProfiles(
            System.Web.Profile.ProfileAuthenticationOption authenticationOption, 
            int pageIndex, int pageSize, out int totalRecords)
        {
            System.Web.Profile.ProfileInfoCollection profileInfoCollection =
               new System.Web.Profile.ProfileInfoCollection();

            IList<System.Web.Profile.ProfileInfo> profileInfos =
                new ProfileService(repository).GetAllProfiles(
                    this.applicationName,
                    authenticationOption,
                    pageIndex, 
                    pageIndex, 
                    out totalRecords);

            foreach (var profileInfo in profileInfos)
            {
                profileInfoCollection.Add(profileInfo);
            }

            return profileInfoCollection;
        }

        /// <summary>
        /// 获取数据源中上次活动日期与指定的 userInactiveSinceDate 相同或在其之前的配置文件的数量。 
        /// </summary>
        /// <param name="authenticationOption">
        /// ProfileAuthenticationOption 值之一，
        /// 指定是返回匿名配置文件、经过身份验证的配置文件还是同时返回这两种类型的配置文件。 
        /// </param>
        /// <param name="userInactiveSinceDate">
        /// 数据源中上次活动日期出现在指定日期和时间之前的配置文件的数量。
        /// </param>
        /// <returns>数据源中上次活动日期出现在指定日期和时间之前的配置文件的数量。</returns>
        public override int GetNumberOfInactiveProfiles(
            System.Web.Profile.ProfileAuthenticationOption authenticationOption,
            DateTime userInactiveSinceDate)
        {
            return new ProfileService(repository).GetNumberOfInactiveProfiles(
                this.applicationName,
                authenticationOption, 
                userInactiveSinceDate);
        }

        /// <summary>
        /// 从配置文件数据库检索配置文件属性信息和值。
        /// </summary>
        /// <param name="sc">包含用户配置文件信息的 SettingsContext。</param>
        /// <param name="properties">
        /// 一个 SettingsPropertyCollection，包含要检索的属性的配置文件信息。 
        /// </param>
        /// <returns>包含配置文件属性信息和值的 SettingsPropertyValueCollection。</returns>
        public override System.Configuration.SettingsPropertyValueCollection GetPropertyValues(
            System.Configuration.SettingsContext sc,
            System.Configuration.SettingsPropertyCollection properties)
        {
            System.Configuration.SettingsPropertyValueCollection settingsPropertyValueCollection =
                new System.Configuration.SettingsPropertyValueCollection();

            if (properties.Count < 1)
            {
                return settingsPropertyValueCollection;
            }

            string username = (string)sc["UserName"];

            foreach (System.Configuration.SettingsProperty settingsProperty in properties)
            {
                if (settingsProperty.SerializeAs
                    == System.Configuration.SettingsSerializeAs.ProviderSpecific)
                {
                    if (settingsProperty.PropertyType.IsPrimitive 
                        || (settingsProperty.PropertyType == typeof(string)))
                    {
                        settingsProperty.SerializeAs = 
                            System.Configuration.SettingsSerializeAs.String;
                    }
                    else
                    {
                        settingsProperty.SerializeAs = 
                            System.Configuration.SettingsSerializeAs.Xml;
                    }
                }
                settingsPropertyValueCollection.Add(
                    new System.Configuration.SettingsPropertyValue(settingsProperty));
            }
            if (!String.IsNullOrEmpty(username))
            {
                new ProfileService(repository).GetProperties(
                    this.applicationName, 
                    username,
                    settingsPropertyValueCollection, 
                    DateTime.UtcNow);
            }


            return settingsPropertyValueCollection;
        }

        /// <summary>
        /// 利用指定的属性值更新配置文件数据库。
        /// </summary>
        /// <param name="sc">包含用户配置文件信息的 SettingsContext。</param>
        /// <param name="properties">
        /// 一个 SettingsPropertyValueCollection，包含要更新的属性的配置文件信息和值。 
        /// </param>
        public override void SetPropertyValues(System.Configuration.SettingsContext sc,
            System.Configuration.SettingsPropertyValueCollection properties)
        {
            string username = (string)sc["UserName"];
            bool isAuthenticated = (bool)sc["IsAuthenticated"];
            if (username == null || username.Length < 1 || properties.Count < 1)
            {
                return;
            }

            new ProfileService(repository).SetProperties(
                this.applicationName, 
                username,
                properties,
                isAuthenticated, 
                DateTime.UtcNow);

        }
    }
}
